package model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;

/**
 * 
 * This class contains all the relevent information and methods pertaining to a
 * continent in Risk. It holds the continent's name, any ownership, all its
 * included territories and a reference to the game map.
 * 
 * @author David Shefchik, Xiaotian Chang
 * 
 */
@SuppressWarnings("serial")
public class Continent extends Observable implements Observer, Serializable {
	private String name;
	private HashMap<Territory, Player> territoryOwnership;
	private ArrayList<Territory> territories;
	private Map map;
	private int bonusTroops;

	/**
	 * Constructor assigns the name and map to each continent, as well as a hash
	 * map for keeping territory ownership consistent. Also creates the list of
	 * territories and adds the map as an observer
	 * 
	 * @param name
	 *            is the name of the continent.
	 * @param map
	 *            is where the continent exist.
	 */
	public Continent(String name, Map map) {
		this.name = name;
		this.map = map;
		this.territoryOwnership = new HashMap<Territory, Player>();
		territories = new ArrayList<Territory>();
		this.addObserver(map);
	}

	/**
	 * Returns the name of this continent
	 * 
	 * @return the name of this continent
	 */
	public String getContinentName() {
		return this.name;
	}

	/**
	 * Returns a reference to this continent's map.
	 * 
	 * @return return the map of this continent
	 */
	public Map getContinentMap() {
		return this.map;
	}

	/**
	 * Returns an ArrayList of all the territories contained within this
	 * continent.
	 * 
	 * @return An ArrayList of all territories in the continent
	 */
	public ArrayList<Territory> listTerritory() {
		return this.territories;
	}

	/**
	 * This method takes in a territory and checks to see if that territory is
	 * contained within this continent. It returns a boolean value indicative of
	 * the call's success
	 * 
	 * @param aTerr
	 *            The Territory whose existence in this continent is to be
	 *            checked.
	 * @return true is the given Territory is in the continent, false otherwise.
	 */
	public boolean hasTerritory(Territory aTerr) {
		return listTerritory().contains(aTerr);
	}

	/**
	 * This method takes in a Territory and adds it to the continent.
	 * 
	 * @param toAdd
	 *            is a Territory to add into this continent.
	 */
	public void addTerritory(Territory toAdd) {
		if (!hasTerritory(toAdd)) {
			this.territoryOwnership.put(toAdd, toAdd.getRuler());
			this.territories.add(toAdd);
			this.hasChanged();
			this.notifyObservers(this);
		}
	}

	/**
	 * NOTICE: to use this method, please make sure Territory inside continent.
	 * 
	 * This method takes a Territory's name as a string and returns a reference
	 * to that territory. If not, found it returns null.
	 * 
	 * @param TerritoryName
	 *            is the name of Territory needed.
	 * @return the Territory with given name.
	 */
	public Territory getTerritoryInContinent(String TerritoryName) {
		ArrayList<Territory> tempList = listTerritory();
		for (Territory temp : tempList) {
			if (temp.getTerritoryName() == TerritoryName) {
				return temp;
			}
		}
		return null;
	}

	/**
	 * This method checks to see if the entire continent is owned by one player.
	 * Returns a boolean indicating yes or no.
	 * 
	 * @return true if this continent is ruled by one Player, false otherwise.
	 */
	public boolean conqueredContinent() {
		ArrayList<Territory> tempList = listTerritory();
		if (tempList.size() <= 0) {
			return false;
		}
		Player tempPlayer = tempList.get(0).getRuler();
		for (Territory tempTerr : tempList) {
			if (!tempPlayer.equals(tempTerr.getRuler())) {
				return false;
			}
		}
		return true;
	}

	/**
	 * NOTICE: to use this method please use conqueredContinent() first to make
	 * sure the continent is conquered by one player.
	 * 
	 * This method returns the player that owns the entire continent, if there
	 * is one.
	 * 
	 * @return the Player that owns all territories in this continent.
	 */
	public Player getContinentRuler() {
		if (conqueredContinent()) {
			return listTerritory().get(0).getRuler();
		} else {
			return null;
		}
	}

	/**
	 * This method updates all the information in the continent when an
	 * observable calls notifyobservers()
	 * 
	 * @param source
	 *            is the class notify observers.
	 * @param value
	 *            is the data changed.
	 */
	public void update(Observable source, Object value) {
		if (source instanceof Territory) {
			Territory newTerr = (Territory) value;
			if (hasTerritory(newTerr)) {
				ArrayList<Territory> tempList = listTerritory();
				for (Territory temp : tempList) {
					if (temp.getTerritoryName().equals(
							newTerr.getTerritoryName())) {
						this.territoryOwnership.remove(temp);
						this.territoryOwnership
								.put(newTerr, newTerr.getRuler());
						this.setChanged();
						this.notifyObservers(this);
					}// if condition end.
				}// for loop end.
			}// if condition to check given Territory is contained end.
		}// if condition to check the source is Territory class end.
	}// update method end.

	/**
	 * Sets the number of bonus troops a player receives each turn if he owns
	 * all the territories in this continent
	 * 
	 * @param bonusTroops
	 *            An integer representing the bonus value of this continent
	 */
	public void setBonusTroops(int bonusTroops) {
		this.bonusTroops = bonusTroops;
	}

	/**
	 * Returns the number of bonus troops a player receives each turn if he owns
	 * all the territories in this continent
	 * 
	 * @return An integer representing the bonus value for ownership of this
	 *         continent
	 */
	public int getBonusTroops() {
		return bonusTroops;
	}
}// all class end.
